package jp.co.systemi.asp.workflow.mail;

/**
 * 有給休暇申請メール受信サーブレット
 */
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.systemi.asp.workflow.data.PaidHoliday;
import jp.co.systemi.asp.workflow.data.User;
import jp.co.systemi.asp.workflow.model.PaidHolidayApplyModel;

import com.google.appengine.api.datastore.Blob;

@SuppressWarnings("serial")
public class MailReceiverServlet extends HttpServlet {
    private static final Logger log = Logger
            .getLogger(MailReceiverServlet.class.getName());
    private static ResourceBundle mailBundle = ResourceBundle
            .getBundle(Mail.class.getName().replace(".", "/"));
    /** HTMLメールファイル名 */
    private int htmlFileNameCount = 0;
    private final static String PAID_HOLIDAY_APPLY = "有給休暇申請";
    List<PaidHoliday> holidays = new ArrayList<PaidHoliday>();

    private String subject = "";
    private String from = "";

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        log.info("メール解析");
        try {

            Properties props = new Properties();
            Session session = Session.getDefaultInstance(props, null);
            MimeMessage message = new MimeMessage(session, req.getInputStream());
            log.info("subject: " + message.getSubject());
            subject = message.getSubject();
            // subject = new
            // String(message.getSubject().getBytes(mailBundle.getString("encoding")),
            // "UTF-8");
            from = ((InternetAddress) message.getFrom()[0]).getAddress();
            log.info("from: " + from);
            for (Address to : message.getRecipients(Message.RecipientType.TO)) {
                log.info("to: " + to.toString());
            }
            if (message.getRecipients(Message.RecipientType.CC) != null) {
                for (Address cc : message
                        .getRecipients(Message.RecipientType.CC)) {
                    log.info("cc: " + cc.toString());
                }
            }
            if (message.getRecipients(Message.RecipientType.BCC) != null) {
                for (Address bcc : message
                        .getRecipients(Message.RecipientType.BCC)) {
                    log.info("bcc: " + bcc.toString());
                }
            }
            /* メール解析 */
            partAnalysis((Part) message);

            if (holidays.size() > 0) {
                User mailSender = new User();
                mailSender.setLoginId(mailBundle.getString("from"));
                mailSender.setFirstName(mailBundle.getString("title"));
                PaidHolidayApplyModel applyProc = new PaidHolidayApplyModel(
                        from, mailSender);
                applyProc.loadHolidays(from);
                try {
                    applyProc.regEditingHolidays(holidays);
                    applyProc.loadMaster();
                    applyProc.loadEditingHolidays();
                    applyProc.loadApprovers();
                    applyProc.onApply();
                    applyProc.processApplies(req, "");
                } catch (Exception e) {
                    for (StackTraceElement ste : e.getStackTrace()) {
                        log.info(ste.getClassName() + " " + ste.getMethodName()
                                + " " + ste.getLineNumber());
                    }
                    log.info("error for regEditingHoliday: "
                            + e.getLocalizedMessage() + " " + e.getMessage());
                    // TODO エラーメール送信
                    applyProc.sendErrorMail(holidays, e.getMessage());
                    return;
                }
            }

        } catch (MessagingException e) {
            log.info(e.getLocalizedMessage());
            // TODO エラーメール送信
        } catch (Exception e) {
            for (StackTraceElement ste : e.getStackTrace()) {
                log.info(ste.getClassName() + " " + ste.getMethodName() + " "
                        + ste.getLineNumber());
            }
            log
                    .info("error: " + e.getLocalizedMessage() + " "
                            + e.getMessage());
            // TODO エラーメール送信
        }
    }

    /**
     * メール解析
     *
     * @param part
     * @throws Exception
     */
    private void partAnalysis(Part part) throws Exception {

        if (part.isMimeType("multipart/*")) {
            /* マルチパートの場合 ------------------------------------------------- */

            Multipart content = (Multipart) part.getContent();

            // 含まれるパートを再帰的に処理
            int count = content.getCount();
            for (int i = 0; i < count; i++) {
                partAnalysis(content.getBodyPart(i));
            }

        } else if (part.isMimeType("text/plain")) {
            /* テキストの場合 ----------------------------------------------------- */

            InputStream is = (InputStream) part.getInputStream();
            Reader r = new InputStreamReader(is, mailBundle
                    .getString("encoding"));
            BufferedReader buf = new BufferedReader(r);

            /* テキスト内容 */
            StringBuilder sb = new StringBuilder();
            for (String line; (line = buf.readLine()) != null;) {
                sb.append(line + "\n");
            }
            log.info("mail contents: " + sb.toString());

            if (PAID_HOLIDAY_APPLY.equals(subject)) {
                PaidHoliday holiday = parsePaidHolidayApply(sb.toString());
                log.info("holiday = " + holiday);
                if (holiday != null) {
                    holidays.add(holiday);
                }
            }
        } else {
            // その他の場合

            /* 添付ファイルを保存 */
            getAttachments(part);
        }

    }

    public static void main(String[] args) {
        String txt = "2010/11/07～2010/11/07 1日間 有給休暇";
        MailReceiverServlet servlet = new MailReceiverServlet();
        PaidHoliday apply = servlet.parsePaidHolidayApply(txt);
        log.info(apply.toString());
    }

    private PaidHoliday parsePaidHolidayApply(String applyString) {
        // 例：
        // 2010/11/07～2010/11/07 1日間　有給休暇
        String patternStr = "(\\d{4,4})[/-](\\d{2,2})[/-](\\d{2,2}).*?(\\d{4,4})[/-](\\d{2,2})[/-](\\d{2,2}).*?(\\d+(\\.\\d*){0,1})日間\\s*(.*)";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(applyString);
        boolean blnMatch = matcher.find();
        log.info("regexp matched? " + blnMatch);
        if (blnMatch) {
            log.info("" + matcher.group(1) + "年" + matcher.group(2) + "月"
                    + matcher.group(3) + "日");
            log.info("" + matcher.group(4) + "年" + matcher.group(5) + "月"
                    + matcher.group(6) + "日");
            log
                    .info(""
                            + matcher.group(7)
                            + (matcher.group(8) == null ? "" : matcher.group(8))
                            + "日間");
            log.info("" + matcher.group(9));
            PaidHoliday apply = new PaidHoliday();
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.YEAR, Integer.parseInt(matcher.group(1)));
            cal.set(Calendar.MONTH, Integer.parseInt(matcher.group(2)) - 1);
            cal.set(Calendar.DATE, Integer.parseInt(matcher.group(3)));
            Date startDate = cal.getTime();

            cal.set(Calendar.YEAR, Integer.parseInt(matcher.group(4)));
            cal.set(Calendar.MONTH, Integer.parseInt(matcher.group(5)) - 1);
            cal.set(Calendar.DATE, Integer.parseInt(matcher.group(6)));
            Date endDate = cal.getTime();

            double days = Double.parseDouble(matcher.group(7)
                    + (matcher.group(8) == null ? "" : matcher.group(8)));

            String comment = matcher.group(9);

            apply.setStartDate(startDate);
            apply.setEndDate(endDate);
            apply.setDays(days);
            apply.setMemo(comment);
            // 申請者を設定
            apply.setLoginId(from);
            return apply;
        }
        return null;
    }

    /**
     * 添付ファイルを取得
     *
     * @param part
     * @throws MessagingException
     * @throws IOException
     */
    private void getAttachments(Part part) throws MessagingException,
            IOException {

        String disp = part.getDisposition();

        if (disp == null || disp.equalsIgnoreCase(Part.ATTACHMENT)) {
            /* 添付ファイルの場合 --------------------------------------------------- */
            log.info("filename " + part.getFileName());
            /* ファイル名 */
            if (part.getFileName() != null) {
                String filename = MimeUtility.decodeText(part.getFileName());
                if (part.isMimeType("text/html")) {
                    filename = String.valueOf(htmlFileNameCount++) + ".html";
                }

                /* ファイルサイズ */
                int fileSize = part.getSize();

                /* コンテントタイプ */
                String contentType = part.getContentType();

                /* ファイル内容 */
                Blob blob = this.generateBlob(part.getInputStream());

                log
                        .info("filename: " + filename + " fileSize: "
                                + fileSize + " contentType: " + contentType
                                + " contents: " + blob);
            }
        }
    }

    /**
     * Blob生成
     *
     * @param in
     * @return Blob
     * @throws IOException
     */
    private Blob generateBlob(InputStream in) throws IOException {

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int len;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer, 0, buffer.length)) != -1) {
            out.write(buffer, 0, len);
        }
        byte[] b = out.toByteArray();

        return new Blob(b);

    }

}
